Skip to content

Conversation

@huoyaoyuan
Copy link
Member

Use List<T> outside type loader, and replace the usage of LowLevelList<T> to bool[] for representing field GC layout.

Separated with LowLevelDictionary<T>, which may introduce more concern about code size and speed.

@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Oct 22, 2024
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas
See info in area-owners.md if you want to be subscribed.

/// <param name="offset">The offset at which we need to write the bitfield.</param>
public void WriteToBitfield(LowLevelList<bool> bitfield, int offset)
/// <returns>The result bitfield, may or may not be the input array.</returns>
public bool[] WriteToBitfield(bool[] bitfield, int offset)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how relevant is this. I can only find one caller of this method which builds ArrayType.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure what you mean by "this"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean the WriteToBitfield method and its descendants. I can only find one caller and its input argument is always empty. The GCLayout structure represents a mergeable bit vector, but I can't find the callers fully leveraging it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to simplify it

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Taking more insight on GCLayout, it's converting two representations back and forth. I'm not fully understood about GC info encoding and not sure whether it should be converted further.

#if TYPE_LOADER_IMPLEMENTATION
[System.Runtime.CompilerServices.ForceDictionaryLookups]
#endif
internal class LowLevelList<T>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the type be moved under TypeLoader since it's only required for it?

Additional question: is ForceDictionaryLookups only applicable for reference types because of generic sharing, or also applicable for value types?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the type be moved under TypeLoader since it's only required for it?

It is very likely that there is code in CoreLib that is used by the TypeLoader indirectly that would need to be annotated with this attribute if we ever resurrected this (#85184 (comment)).

I think it would be ok to delete ForceDictionaryLookups and its few remaining uses. It is likely heavily bitrotten.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, in this PR I was using Array.Resize which is generic. It seems that the real foreseeing solution is to limit TypeLoader usage of CoreLib, according to the linked discussion. It also means that TypeLoader needs its specialized collection. If we resurrected the generic lookup work, is it expected to use the attribute, or just do some specialized handling for TypeLoader?

The original question is that, would there be any place else (not involved with TypeLoader) unable to use the CoreLib collection? I assume no.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per #85184 (comment), if we ever resurrected the lazy generic lookup work, we would need likely ended up with TypeLoader in its own partition that does not perform the lazy generic lookups. It would be very large refactoring.

/// The dictionary's value is a list of (resourcename,index) tuples.
/// </summary>
private static volatile LowLevelDictionary<string, LowLevelList<ResourceInfo>> s_extractedResourceDictionary;
private static volatile LowLevelDictionary<string, List<ResourceInfo>> s_extractedResourceDictionary;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In avialonia-linux, all interfaces on the List<ResourceInfo> are preserved. This is causing the majority of size difference.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Appearantly the usual List and Dictionary do have higher size impact than LowLevelList and LowLevelDictionary, but they are very likely to be used by other part of application, especially Dictionary<__Canon, __Canon>.

@agocke
Copy link
Member

agocke commented Mar 3, 2025

cc @MichalStrehovsky

@huoyaoyuan
Copy link
Member Author

Triaging the status of this PR: we should first make the footprint for List<ValueType> smaller so that it can be adopted more easily. We may also switch element types to class for cold path, as List<ReferenceType> is expected to be extremely common.

private static uint s_genericFunctionPointerNextIndex;
private const uint c_genericDictionaryChunkSize = 1024;
private static LowLevelList<IntPtr> s_genericFunctionPointerCollection = new LowLevelList<IntPtr>();
private static readonly List<IntPtr> s_genericFunctionPointerCollection = new List<IntPtr>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This s_genericFunctionPointerCollection pool looks like a premature optimization. I think it would be fine to allocate GenericMethodDescriptorInfos one at a time.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean there will be memory leak per function pointer, instead of per method instantiation? This would become unbounded instead of bounded.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s_genericFunctionPointerCollection does not enable any GenericMethodDescriptor reuse. There is s_genericFunctionPointerDictionary for that. I am not suggesting deleting s_genericFunctionPointerDictionary.

s_genericFunctionPointerCollection reduces frequency of NativeMemory.Alloc calls. We allocate spaces for c_genericDictionaryChunkSize GenericMethodDescriptors at a time using NativeMemory.Alloc and then hand them out one at a time.

@jkotas
Copy link
Member

jkotas commented May 26, 2025

Triaging the status of this PR: we should first make the footprint for List smaller so that it can be adopted more easily.

That's very hard.

I would split this PR into multiple smaller ones that are easier to discuss, review and merge:

  • Delete ForceDictionaryLookups attribute from everywhere
  • bool[] refactoring in type loader
  • ...

{
if (s_extractedResourceDictionary == null)
{
// Lazily create the extracted resource dictionary. If two threads race here, we may construct two dictionaries
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the question to ask here is whether we need to create this global cache in the first place.

@agocke
Copy link
Member

agocke commented May 31, 2025

I'm going to move this to draft for now. Can be resubmitted with more granular changes.

@dotnet-policy-service
Copy link
Contributor

Draft Pull Request was automatically closed for 30 days of inactivity. Please let us know if you'd like to reopen it.

@huoyaoyuan huoyaoyuan deleted the lowlevel-list-2 branch July 2, 2025 06:09
@github-actions github-actions bot locked and limited conversation to collaborators Aug 5, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-NativeAOT-coreclr community-contribution Indicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants